#pragma once

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <type_traits>

#if defined(_MSC_VER )
#pragma pack(push)
#pragma pack(1)
#else
#pragma pack(1) 
#endif // defined(_MSC_VER)

// protocol header
struct ProtocolHeader {
    std::uint32_t length; // packet length include header
    std::uint32_t msgID; // message ID
    // host endian to network endian
    void hton();
    // network endian to host endian
    void ntoh();
    // ctor
    ProtocolHeader();
    // ctor
    // @param data stream
    ProtocolHeader(const char* address);
};

struct ProtocolLocatorHeader {
    std::uint32_t length; // packet length include header
    std::uint32_t msgID; // message ID
    std::uint32_t locatorMsgLength; // locator message length
    // host endian to network endian
    void hton();
    // network endian to host endian
    void ntoh();
    // ctor
    ProtocolLocatorHeader();
    // ctor
    // @param data stream
    ProtocolLocatorHeader(const char* address);
    // Builds a header in data block
    static void toBuffer(char* address, std::uint32_t length, std::uint32_t msgID, std::uint32_t locatorMsgLength);
};

enum class LocatorMsgID : std::uint32_t {
    FIRST_MSG_ID = 0xFFFFFF00,

    //
    // ENTITY ->
    //
    ENTITY_MSG_BEGIN,
    REGISTER_REQ,
    REGISTER_ACK,
    UNREGISTER_REQ,
    UNREGISTER_ACK,
    RELAY_REQ,
    RELAY_ACK,
    SEARCH_REQ,
    SEARCH_ACK,
    SEARCH_NTF,
    ENTITY_MSG_END,
    //
    // <- ENTITY
    //

    //
    // RPC ->
    //
    RPC_MSG_BEGIN,
    RPC_REQ_PATH,
    RPC_ACK_PATH,
    RPC_REQ_SITE,
    RPC_ACK_SITE,
    RPC_REQ_ENTITY,
    RPC_ACK_ENTITY,
    RPC_MSG_END,
    //
    // <- RPC
    //

    LAST_MSG_ID = 0xFFFFFFFF,
};

#if defined(_MSC_VER )
#pragma pack(pop)
#else
#pragma pack()
#endif // defined(_MSC_VER)

using MsgIDRealType = std::underlying_type<LocatorMsgID>::type;
static inline MsgIDRealType getMsgRealID(LocatorMsgID msgID) {
    return static_cast<MsgIDRealType>(msgID);
}
